package persistence;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;

import persistence.annotation.Column;
import persistence.annotation.Id;

public class PersistentProperty {

	/**
	 * Le nom de l'attribut, par exemple « title » pour Book
	 */
	private String name;

	/**
	 * La classe java de cet attribut
	 */
	private Class<?> clazz;

	/**
	 * Le nom de la colonne dans la table, par défaut le nom de l'attribut
	 */
	private String columnName;

	/**
	 * Le type SQL de la colonne, par exemple VARCHAR(255)
	 */
	private String sqlType;

	/**
	 * true si la propriété est l'id
	 */
	private boolean id;

	private Method method;

	private PersistentClass persistentClass;

	public PersistentProperty(PersistentClass persistentClass, Method method) {
		this.persistentClass = persistentClass;
		this.method = method;
		this.parse();
	}

	public void parse() {
		String methodName = method.getName();
		String propertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
		setName(propertyName);
		Class<?> rclazz = method.getReturnType(); // Types non objet ??
		String javaType = rclazz.getSimpleName();
		setClazz(rclazz);

		Column column = method.getAnnotation(Column.class);
		if (column != null && column.name().length() != 0) {
			setColumnName(column.name());
		} else {
			setColumnName(propertyName);
		}

		if (column != null && column.type().length() != 0) {
			setSqlType(column.type());
		} else {
			setSqlType(java2sql.get(javaType));
		}
		Id id = method.getAnnotation(Id.class);
		if (id != null) {
			setId(true);
			persistentClass.setId(this);
			setSqlType(" BIGINT GENERATED ALWAYS AS IDENTITY"); // A généraliser
		}
	}

	private static Map<String, String> java2sql = new HashMap<String, String>() {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		{
			put("String", "VARCHAR(255)");
			put("BigDecimal", "NUMERIC");
			put("Long", "BIGINT");
			put("int", "INT");
		}
	};

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the clazz
	 */
	public Class<?> getClazz() {
		return clazz;
	}

	/**
	 * @param clazz
	 *            the clazz to set
	 */
	public void setClazz(Class<?> clazz) {
		this.clazz = clazz;
	}

	/**
	 * @return the columnName
	 */
	public String getColumnName() {
		return columnName;
	}

	/**
	 * @param columnName
	 *            the columnName to set
	 */
	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}

	/**
	 * @return the sqlType
	 */
	public String getSqlType() {
		return sqlType;
	}

	/**
	 * @param sqlType
	 *            the sqlType to set
	 */
	public void setSqlType(String sqlType) {
		this.sqlType = sqlType;
	}

	/**
	 * @return the id
	 */
	public boolean isId() {
		return id;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(boolean id) {
		this.id = id;
	}

	/**
	 * @param bean
	 * @return
	 * @throws PersistenceException
	 */
	public Object getValue(Object bean) throws PersistenceException {
		try {
			return PropertyUtils.getSimpleProperty(bean, getName());
		} catch (Exception e) {
			throw new PersistenceException("Cannot get property " + getName() + " on object of class " + bean);
		}

	}

	/**
	 * @param bean
	 * @param value
	 * @throws PersistenceException
	 */
	public void setValue(Object bean, Object value) throws PersistenceException {
		Object valueConverted = ConvertUtils.convert(value, getClazz());
		try {
			PropertyUtils.setSimpleProperty(bean, getName(), valueConverted);
		} catch (Exception e) {
			throw new PersistenceException("Cannot set property " + getName() + " on object of class " + bean.getClass());
		}
	}

	/**
	 * @return the method
	 */
	public Method getMethod() {
		return method;
	}

	/**
	 * @param method
	 *            the method to set
	 */
	public void setMethod(Method method) {
		this.method = method;
	}

}
